﻿using LCL.Network.IMessage;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace LCL.Network
{
    /// <summary>
    /// 服务器程序的基础类
    /// </summary>
    public class NetworkServerBase : NetworkXBase
    {
        #region Constructor
        /// <summary>
        /// 实例化一个默认的对象
        /// </summary>
        public NetworkServerBase()
        {
            IsStarted = false;
            Port = 0;
        }
        #endregion
        #region Public Properties
        /// <summary>
        /// 服务器引擎是否启动
        /// </summary>
        public bool IsStarted { get; protected set; }
        /// <summary>
        /// 服务器的端口号
        /// </summary>
        /// <remarks>需要在服务器启动之前设置为有效</remarks>
        public int Port { get; set; }
        #endregion
        #region Protect Method
        /// <summary>
        /// 异步传入的连接申请请求
        /// </summary>
        /// <param name="iar"></param>
        protected void AsyncAcceptCallback(IAsyncResult iar)
        {
            //还原传入的原始套接字
            Socket server_socket = (Socket)iar.AsyncState;
            Socket client = null;
            try
            {
                // 在原始套接字上调用EndAccept方法，返回新的套接字
                client = server_socket.EndAccept(iar);
                ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolLogin), client);
            }
            catch (ObjectDisposedException)
            {
                // 服务器关闭时候触发的异常，不进行记录
                return;
            }
            catch (Exception ex)
            {
                // 有可能刚连接上就断开了，那就不管
                client.Close();
                LogNet.Exception(ToString(), ex);
            }
            // 如果失败，尝试启动三次
            int i = 0;
            while (i < 3)
            {
                try
                {
                    server_socket.BeginAccept(new AsyncCallback(AsyncAcceptCallback), server_socket);
                    break;
                }
                catch (Exception ex)
                {
                    Thread.Sleep(1000);
                    LogNet.Exception(ToString(), ex);
                    i++;
                }
            }
            if (i >= 3)
            {
                // 抛出异常，终止应用程序
                throw new Exception("重新异步接受传入的连接尝试");
            }
        }
        /// <summary>
        /// 用于登录的回调方法
        /// </summary>
        /// <param name="obj">socket对象</param>
        protected virtual void ThreadPoolLogin(object obj)
        {
            Socket socket = obj as Socket;
            socket.Close( );
        }
        #endregion
        #region Start And Close
        /// <summary>
        /// 服务器启动时额外的初始化信息
        /// </summary>
        /// <remarks>需要在派生类中重写</remarks>
        protected virtual void StartInitialization()
        {
        }
        /// <summary>
        /// 启动服务器的引擎
        /// </summary>
        /// <param name="port">指定一个端口号</param>
        public virtual void ServerStart(int port)
        {
            if (!IsStarted)
            {
                StartInitialization( );
                CoreSocket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
                CoreSocket.Bind( new IPEndPoint( IPAddress.Any, port ) );
                CoreSocket.Listen( 500 );//单次允许最后请求500个，足够小型系统应用了
                CoreSocket.BeginAccept( new AsyncCallback( AsyncAcceptCallback ), CoreSocket );
                IsStarted = true;
                Port = port;
                LogNet.Info(ToString() + "启动服务器的引擎");
            }
        }
        /// <summary>
        /// 使用已经配置好的端口启动服务器的引擎
        /// </summary>
        public void ServerStart()
        {
            ServerStart(Port);
        }
        /// <summary>
        /// 服务器关闭的时候需要做的事情
        /// </summary>
        protected virtual void CloseAction()
        {
        }
        /// <summary>
        /// 关闭服务器的引擎
        /// </summary>
        public virtual void ServerClose()
        {
            if (IsStarted)
            {
                CloseAction( );
                CoreSocket.Close( );
                IsStarted = false;
                LogNet.Info(ToString() + "关闭服务器的引擎");
            }
        }
        #endregion
        #region Create Alien Connection
        /**************************************************************************************************
         * 
         *    此处实现了连接Hsl异形客户端的协议，特殊的协议实现定制请联系作者
         *************************************************************************************************/
        /// <summary>
        /// 创建一个指定的异形客户端连接，使用Hsl协议来发送注册包
        /// </summary>
        /// <param name="ipAddress">Ip地址</param>
        /// <param name="port">端口号</param>
        /// <param name="dtuId">设备唯一ID号，最长11</param>
        /// <returns>是否成功连接</returns>
        public Result ConnectHslAlientClient(string ipAddress, int port, string dtuId)
        {
            if (dtuId.Length > 11) dtuId = dtuId.Substring( 11 );
            byte[] sendBytes = new byte[28];
            sendBytes[0] = 0x48;
            sendBytes[1] = 0x73;
            sendBytes[2] = 0x6E;
            sendBytes[4] = 0x17;
            Encoding.ASCII.GetBytes( dtuId ).CopyTo( sendBytes, 5 );
            // 创建连接
            Result<Socket> connect = CreateSocketAndConnect(ipAddress, port, TimeOut);
            if (!connect.Success) return connect;
            // 发送数据
            Result send = Send( connect.Data, sendBytes );
            if (!send.Success) return send;
            // 接收数据
            Result<AlienMessage> receive = ReceiveMessage(connect.Data, TimeOut, new AlienMessage());
            if (!receive.Success) return receive;
            switch (receive.Data.ContentBytes[0])
            {
                case 0x01:
                    {
                        connect.Data.Close();
                        return new Result("当前设备的id重复登录");
                    }
                case 0x02:
                    {
                        connect.Data.Close();
                        return new Result("当前设备的id禁止登录");
                    }
                case 0x03:
                    {
                        connect.Data.Close();
                        return new Result("密码验证失败");
                    }
            }
            ThreadPoolLogin(connect.Data);
            return Result.CreateSuccessResult();
        }
        #endregion
    }
}
